import { error } from "itty-router";

export class KVError extends Error {}

interface KVId {
  id: string;
}

interface KVMetadata extends KVId {
  size: number;
}

export class KV {
  private static maxPartSize = 25 * 1024 * 1024;

  constructor(readonly kv: KVNamespace) {}

  public async list(prefix: string) {
    const result = new Set<string>();
    let part = await this.kv.list({ prefix });
    while (!part.list_complete) {
      part.keys
        .map((key) => key.name)
        .map((key) => key.substring(0, key.lastIndexOf(".")))
        .forEach(result.add);
      part = await this.kv.list({ cursor: part.cursor });
    }
    return [...result];
  }

  private static partKey(key: string, index: number) {
    return `${key}.${index}`;
  }

  private async getPart(
    key: string,
    index: 0
  ): Promise<KVNamespaceGetWithMetadataResult<string, KVMetadata>>;
  private async getPart(
    key: string,
    index: number
  ): Promise<KVNamespaceGetWithMetadataResult<string, KVId>>;
  private async getPart(key: string, index: number) {
    return await this.kv.getWithMetadata(KV.partKey(key, index));
  }

  public async get<T>(key: string): Promise<T | undefined> {
    const part0 = await this.getPart(key, 0);
    if (part0.metadata === null) {
      return undefined;
    }
    let value = "";
    for (let i = 0; i < part0.metadata.size; i++) {
      const keyI = KV.partKey(key, i);
      const partI = i === 0 ? part0 : await this.getPart(key, i);
      if (partI.metadata === null) {
        throw new KVError(`数据损坏，缺少元数据：${keyI}`);
      } else if (partI.value === null) {
        throw new KVError(`数据损坏，缺少数据块：${keyI}`);
      } else if (partI.metadata.id !== part0.metadata.id) {
        throw new KVError(`数据损坏，数据块不属于该数据：${keyI}`);
      }
      value += partI.value;
    }
    return JSON.parse(value);
  }

  private async deleteFrom(key: string, index: number) {
    const part0 = await this.getPart(key, 0);
    if (part0 === null || part0.metadata === null) {
      return;
    }
    for (let i = index; i < part0.metadata.size; i++) {
      await this.kv.delete(KV.partKey(key, i));
    }
  }

  public static partSize(value: string) {
    return Math.ceil(value.length / KV.maxPartSize);
  }

  public async put<T>(key: string, t: T) {
    const value = JSON.stringify(t);
    const id = crypto.randomUUID();
    const size = KV.partSize(value);
    await this.deleteFrom(key, size);
    for (let i = 0; i < size; i++) {
      const keyI = KV.partKey(key, i);
      const valueI = value.slice(i * KV.maxPartSize, (i + 1) * KV.maxPartSize);
      const metadataI = i == 0 ? { id, size } : { id };
      await this.kv.put(keyI, valueI, { metadata: metadataI });
    }
  }

  public async delete(key: string) {
    await this.deleteFrom(key, 0);
  }
}

export function getBy<T, K extends keyof T>(ts: T[], key: K, value: T[K]) {
  const result = ts.filter((t) => t[key] === value);
  if (result.length > 1) {
    throw error(500, `筛选对象失败，键 ${key.toString()} 不唯一`);
  }
  return result.at(0);
}
